package sk.naive.talker.tcpadapter;

import sk.naive.talker.callback.*;
import sk.naive.talker.util.*;
import sk.naive.talker.adapter.TagConsts;
import sk.naive.talker.message.DefaultMessageFactory;

import java.rmi.RemoteException;
import java.util.*;

/**
 * LineEditor.
 * @author <a href="mailto:virgo@naive.deepblue.sk">Richard "Virgo" Richter</a>
 * @version $Revision: 1.10 $ $Date: 2005/01/25 21:57:05 $
 */
public class LineEditor implements IdentifiedCallback {
	private static Map<String,LineEditorCommand> commands;
	private int line;
	private List<String> text;
	private TCPUser user;
	private String callbackId;
	private boolean finalDialog;
	private boolean finish;

	public LineEditor(TCPUser user, String callbackId) {
		this.user = user;
		this.callbackId = callbackId;
		line = 1;
		text = new LinkedList<String>();
	}

	public void start(String originalText) throws RemoteException {
		user.sendMessage("editor.start");
		if (originalText != null && originalText.length() > 0) {
			String[] sa = originalText.split(Utils.tag(TagConsts.BR));
			for (String s : sa) {
				text.add(s);
				line++;
			}
			show(true);
		}
		prompt();
	}

	public Callback process(String s)
		throws CallbackException
	{
		try {
			if (finalDialog) {
				processFinalDialog(s);
			} else {
				if (s.startsWith(".")) {
					processEditorCommand(s.substring(1));
				} else {
					addLine(s);
				}
			}
			if (finish) {
				return null;
			}
			prompt();
		} catch (RemoteException e) {
			throw new CallbackException(e);
		}
		return this;
	}

	private void processEditorCommand(String s) throws RemoteException {
		if (s.length() > 0 && s.charAt(0) == '.') {
			addLine(s);
			return;
		}
		String[] sa = Utils.splitWords(s, 2);
		String comm = sa[0];
		String param = "";
		if (sa.length > 1) {
			param = sa[1];
		}
		for (Map.Entry<String,LineEditorCommand> entry : commands.entrySet()) {
			if (entry.getKey().startsWith(comm)) {
				entry.getValue().process(this, param);
				return;
			}
		}
	}

	private void processFinalDialog(String s) throws RemoteException {
		s = user.resolveResponse("editor.finalPromptResolver", s);
		if (s.equals("save")) {
			save();
		}
		if (s.equals("cancel")) {
			cancel();
		}
		if (s.equals("show")) {
			show(false);
		}
		if (s.equals("continue")) {
			finalDialog = false;
		}
	}

	private void prompt() throws RemoteException {
		if (finalDialog) {
			user.sendMessage("editor.finalPrompt");
		} else {
			user.getProperties().put(DefaultMessageFactory.CTXKEY_VAL, line);
			user.sendMessage("editor.prompt");
		}
	}

	private void addLine(String s) {
		text.add(line - 1, s);
		line++;
	}

	private void save() throws RemoteException {
		user.processUserCallback(new CallbackOkResult(callbackId, textString(false)));
		finish = true;
	}

	private String textString(boolean lineNumbers) {
		StringBuilder sb = new StringBuilder();
		int line = 1;
		for (String s : text) {
			if (sb.length() > 0) {
				sb.append(Utils.tag(TagConsts.BR));
			}
			if (lineNumbers) {
				user.getProperties().put(DefaultMessageFactory.CTXKEY_VAL, line);
				sb.append(getString("editor.lineNumber"));
			}
			sb.append(s);
			line++;
		}
		return sb.toString();
	}

	private String getString(String key) {
		return TCPUser.messageFactory.getString(key, user.getProperties());
	}

	private void cancel() throws RemoteException {
		user.processUserCallback(new CallbackCancelResult(callbackId));
		finish = true;
	}

	private void show(boolean lineNumbers) throws RemoteException {
		user.send(textString(lineNumbers) + Utils.tag(TagConsts.BR));
	}

	public String getId() {
		return callbackId;
	}

	private void redo(int from) {
		from = correctLineNumber(from);
		line = from;
		from--;
		while (text.size() > from) {
			text.remove(from);
		}
	}

	private int correctLineNumber(int from) {
		if (from > (text.size() + 1)) {
			from = text.size() + 1;
		}
		if (from < 1) {
			from = 1;
		}
		return from;
	}

	private void delete(int from, int to) {
		from = correctLineNumber(from);
		to = correctLineNumber(to);
		for (int i = from; i <= to && from <= text.size(); i++) {
			text.remove(from - 1);
			if (line > from) {
				line--;
			}
		}
	}

	private void insert(int from) {
		line = correctLineNumber(from);
	}

	private void append() {
		line = correctLineNumber(text.size() + 1);
	}

	private interface LineEditorCommand {
		void process(LineEditor editor, String s) throws RemoteException;
	}

	static {
		commands = new LinkedHashMap<String,LineEditorCommand>();
		commands.put("", new LineEditorCommand() {
			public void process(LineEditor editor, String s) {
				editor.finalDialog = true;
			}
		});
		commands.put("save", new LineEditorCommand() {
			public void process(LineEditor editor, String s) throws RemoteException {
				editor.save();
			}
		});
		commands.put("cancel", new LineEditorCommand() {
			public void process(LineEditor editor, String s) throws RemoteException {
				editor.cancel();
			}
		});
		commands.put("show", new LineEditorCommand() {
			public void process(LineEditor editor, String s) throws RemoteException {
				editor.show(false);
			}
		});
		commands.put("review", new LineEditorCommand() {
			public void process(LineEditor editor, String s) throws RemoteException {
				editor.show(true);
			}
		});
		commands.put("help", new LineEditorCommand() {
			public void process(LineEditor editor, String s) throws RemoteException {
				editor.user.sendMessage("editor.help");
			}
		});
		commands.put("redo", new LineEditorCommand() {
			public void process(LineEditor editor, String s) throws RemoteException {
				try {
					int from = 1;
					if (s != null && s.trim().length() > 0) {
						from = Integer.parseInt(s);
					}
					editor.redo(from);
				} catch (NumberFormatException e) {
					editor.user.sendMessage("editor.invalidNumberOrRange");
				}
			}
		});
		commands.put("delete", new LineEditorCommand() {
			public void process(LineEditor editor, String s) throws RemoteException {
				try {
					int from = 1;
					int to = editor.text.size();
					if (!deleteAllSpecified(s)) {
						String[] sa = Utils.split(s, "-", 2);
						from = Integer.parseInt(sa[0]);
						if (sa.length > 1) {
							to = Integer.parseInt(sa[1]);
						} else {
							to = from;
						}
					}
					editor.delete(from, to);
				} catch (NumberFormatException e) {
					editor.user.sendMessage("editor.invalidNumberOrRange");
				}
			}
			private boolean deleteAllSpecified(String s) {
				return (s.length() == 0 || s.equalsIgnoreCase("all"));
			}
		});
		commands.put("insert", new LineEditorCommand() {
			public void process(LineEditor editor, String s) throws RemoteException {
				try {
					editor.insert(Integer.parseInt(s));
				} catch (NumberFormatException e) {
					editor.user.sendMessage("editor.invalidNumberOrRange");
				}
			}
		});
		commands.put("append", new LineEditorCommand() {
			public void process(LineEditor editor, String s) {
				editor.append();
			}
		});
	}
}
